/**
 * 正弦类
 *  y = a * sin(b * x + c) + d
 * 加上起始和结束位置 start, end
 * 取点的间隔
 */
class Line {
  constructor(a, b, c, d, z, start, end, gap) {
    this.a = a;
    this.b = b;
    this.c = c;
    this.d = d;
    this.z = z;
    this.start = start;
    this.end = end;
    this.gap = gap;
    this.pointList = [];
    this.computePointList();
  }

  // 计算正弦路径上点列表
  computePointList() {
    this.pointList = [];
    for(let i = this.start; i <= this.end; i = i + this.gap) {
      let x = i;
      let y = this.a * Math.sin((this.b * x + this.c) / 180 * Math.PI) + this.d;
      let offset = i;
      this.pointList.push({ x, y, z: this.z, offset, originX: x })
    }
  }

  // 基于偏移量的点位置更新
  updatePointList(rotationAngleSpeed, observe) {
    this.pointList.forEach(item => {
      let x = item.x;
      let z = item.z;
      item.y = this.a * Math.sin((this.b * item.originX + this.c + item.offset) / 180 * Math.PI) + this.d;
      /**
       * 对于任一点D(xD，yD，zD)，其绕y轴旋转β角的时候，
       * 它的三维坐标变为(xDcosβ-zDsinβ，yD，zDcosβ+xDsinβ)
       */
      const beta = rotationAngleSpeed / 180 * Math.PI;
      item.x = x * Math.cos(beta) - z * Math.sin(beta);
      item.z = z * Math.cos(beta) + x * Math.sin(beta);

      /**
       * 从空间内的任意点A(xA，yA，zA)观察空间内的任一点G(xG，yG，zG)，它在xy平面内的投影H的坐标为
       * ( (xG - xA) * zA / (zA - zG) , (yG - yA) * zA / (zA - zG) )
       */
      item.canvasX = (x - A.x) * A.z / (A.z - z);
      item.canvasY = (item.y - A.y) * A.z / (A.z - z);
      // 这二者便是经过投影变换后的点坐标，是最终要绘制到画布上的坐标
    })
  }
}

/**
 * 页面主逻辑
 */
const c = document.getElementById('c'),
      ctx = c.getContext('2d');
c.width = innerWidth * devicePixelRatio;
c.height = innerHeight * devicePixelRatio;
const A = { x: 0, y: -100, z: 1000 }; // 观察点
const lineList = [
  new Line(20, 2, 0, 0, -150, -200, 200, 10),
  new Line(20, 2, 0, 0, -120, -200, 200, 10),
  new Line(20, 2, 0, 0, -90, -200, 200, 10),
  new Line(20, 2, 0, 0, -60, -200, 200, 10),
  new Line(20, 2, 0, 0, -30, -200, 200, 10),
  new Line(20, 2, 0, 0, 0, -200, 200, 10),
  new Line(20, 2, 0, 0, 30, -200, 200, 10),
  new Line(20, 2, 0, 0, 60, -200, 200, 10),
  new Line(20, 2, 0, 0, 90, -200, 200, 10),
  new Line(20, 2, 0, 0, 120, -200, 200, 10),
  new Line(20, 2, 0, 0, 150, -200, 200, 10),
]; // 正弦波浪线列表
let lineOffset = 1;

// 绘制函数
const draw = () => {
  ctx.clearRect(0, 0, c.width, c.height);
  lineList.forEach(line => {
    line.pointList.forEach(item => {
      ctx.beginPath();
      /**
       * 在三维模型中，近大远小是非常重要的视觉特征，
       * 假定小圆点G(xG, yG, zG)的半斤是R，站在A(xA, yA, zA)点观测小圆点位于平面xy上投影的半径为
       * nR = (R * zA) / (zA - zG)
       */
      const size = 2 * A.z / (A.z - item.z);
      ctx.arc(item.canvasX + c.width / 2, item.canvasY + c.height / 2, size, 0, 2 * Math.PI);
      ctx.closePath();
      ctx.fill();
    })
  })
}

// 动画
const animate = () => {
  lineList.forEach((line, i) => {
    line.c = lineOffset + i * 30;
    line.updatePointList(.5, A);
  })
  lineOffset += 1;
  draw();
  requestAnimationFrame(animate);
}
animate();
